package com.nhimeye.admin.app;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import org.vaadin.addon.customfield.ConvertingValidator;
import org.vaadin.addon.customfield.PropertyConverter;

import com.nhimeye.domain.User;
import com.vaadin.addon.beanvalidation.BeanValidationValidator;
import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.data.Validator;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.Component;
import com.vaadin.ui.CustomComponent;
import com.vaadin.ui.Field;
import com.vaadin.ui.TextField;

public abstract class EntityEditor<T> extends CustomComponent implements Component {
	
	
	
	private static final long serialVersionUID = 1L;

	private Map<Object, Field> fieldMap = new LinkedHashMap<Object, Field>();
    
    @SuppressWarnings("rawtypes")
	private Map<Object, PropertyConverter> converterMap = new LinkedHashMap<Object, PropertyConverter>();
    
    public Field getField(Object propertyId) {
        return fieldMap.get(propertyId);
    }
    
    
    
	 public void configure() {
	        configureFieldMap(fieldMap);
	        configureFields();
	        configureContainersForFields();
	        configureConverters();
	        configureValidators();
	    }
	    
	  
	    
	    public void validateFields() {
	        if (getItemDataSource() != null) {
	            for (Object propertyId : getItemDataSource().getItemPropertyIds()) {
	                Field field = getField(propertyId);
	                if (field != null && !field.isReadOnly()) {
	                    field.validate();
	                }
	            }
	        }
	    }
	    
	    public void commitFields() {
	        if (getItemDataSource() != null) {
	            for (Object propertyId : getItemDataSource().getItemPropertyIds()) {
	                Field field = getField(propertyId);
	                if (field != null && !field.isReadOnly()) {
	                    field.commit();
	                }
	            }
	        }
	    }
	    
	    public void setFieldPropertyDataSource(Object propertyId, Property property) {
	        Field field = getField(propertyId);
	        if (field == null) {
	            return;
	        }
	        if (property == null) {
	            field.setPropertyDataSource(null);
	        } else {
	            @SuppressWarnings("rawtypes")
				PropertyConverter converter = getConverter(propertyId);
	            if (converter != null) {
	                converter.setPropertyDataSource(property);
	                field.setPropertyDataSource(converter);
	            } else {
	                if (field instanceof CheckBox && property.getValue() == null) {
	                    property.setValue(Boolean.FALSE);
	                }
	                field.setPropertyDataSource(property);
	            }
	        }
	    }
	    
	    public void setFieldValues(Item item) {
	        if (item != null) {
	            // set values for fields in item
	            for (Object propertyId : item.getItemPropertyIds()) {
	                setFieldPropertyDataSource(propertyId, item.getItemProperty(propertyId));
	            }
	            // other fields are not touched by default
	        } else {
	            // reset all fields
	            for (Object propertyId : getBeanPropertyIds()) {
	                setFieldPropertyDataSource(propertyId, null);
	            }
	        }
	    }
	    
	    public Field getFirstField() {
	        Iterator<Object> it = getBeanPropertyIds().iterator();
	        if (it.hasNext()) {
	            return getField(it.next());
	        }
	        return null;
	    }
	    
	    public void configureFields() {
	        for (Object propertyId : getBeanPropertyIds()) {
	            Field field = getField(propertyId);
	            if (field == null) {
	                continue;
	            }
	            if (field instanceof TextField) {
	                ((TextField) field).setNullRepresentation("");
	            }
	            field.setWriteThrough(false);
	            field.setInvalidAllowed(true);
	        }
	    }
	    
	    public void configureContainersForFields() {
	        // no fields require special containers
	    }
	    
	    public void configureConverters() {
	        // no converters needed
	    }
	    
	    @SuppressWarnings({ "rawtypes", "unchecked" })
		public void configureValidators() {
	        for (Object propertyId : getBeanPropertyIds()) {
	            Field field = getField(propertyId);
	            if (field != null) {
	                Collection<Validator> validators = field.getValidators();
	                if (validators != null) {
	                    for (Validator validator : new ArrayList<Validator>(field.getValidators())) {
	                        if (validator instanceof BeanValidationValidator || validator instanceof ConvertingValidator) {
	                            field.removeValidator(validator);
	                        }
	                    }
	                }
	                BeanValidationValidator validator = new BeanValidationValidator(getEntityClass(), String.valueOf(propertyId));
	                if (validator.isRequired()) {
	                    field.setRequired(true);
	                    field.setRequiredError(validator.getRequiredMessage());
	                }
	                PropertyConverter converter = getConverter(propertyId);
	                if (converter == null) {
	                    field.addValidator(validator);
	                } else {
	                    field.addValidator(new ConvertingValidator(validator, converter));
	                }
	            }
	        }
	    }
	    
	    @SuppressWarnings("rawtypes")
		public PropertyConverter getConverter(Object propertyId) {
	        return converterMap.get(propertyId);
	    }
	    
	protected  abstract Collection<Object> getBeanPropertyIds();
	
	protected abstract void configureFieldMap(Map<Object, Field> fieldMap) ;
	
	protected abstract Class<T> getEntityClass() ;

    public abstract void addSaveActionListener(ClickListener listener);

    public abstract void addCancelActionListener(ClickListener listener);

    public abstract void addDeleteActionListener(ClickListener listener);

    public abstract void setSaveAllowed(boolean enabled);

    public abstract void setDeleteAllowed(boolean enabled);

    public abstract void commit();

    public abstract void setItemDataSource(Item item);

    public abstract Item getItemDataSource();

    public abstract void setCommitErrorMessage(String message);

    public abstract void focus();

    public abstract void refresh();

    public abstract boolean isModified();

}
